﻿using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.ComponentModel;
using System.Runtime.Serialization;
using System.Collections.ObjectModel;
using System.Windows.Data;
using System.Linq;
using System.Collections.Generic;
using System.IO.IsolatedStorage;
using System.IO;
using System.Runtime.Serialization.Json;
using System.Text;
using Microsoft.Phone.Controls.Maps;
using System.Device.Location;
using System.Diagnostics;

namespace Strava1
{
    /*[DataContract]
    public class Rides
    {
        public Rides()
        {
            RidesColl = new ObservableCollection<Ride>();
        }

        [DataMember(Name = "rides")]
        public ObservableCollection<Ride> RidesColl { get; set; }
    }*/

    [DataContract]
    public class Ride : INotifyPropertyChanged
    {
        public Ride()
        {
            FullDetailsLoaded = false;
            //SegmentsCVS = new CollectionViewSource();
            //SegmentsCVS.Source = Segments;
            EffortsCVS = new CollectionViewSource();
            EffortsCVS.Source = Efforts;
            IsDirty = true;
            IsEffortsDirty = true;
        }

        public bool IsDirty { get; set; }
        public bool IsEffortsDirty { get; set; }

        [DataMember(Name = "id")]
        public int Id { get; set; }

        [DataMember(Name = "name")]
        public string Name { get; set; }

        [DataMember(Name = "effortsCount")]
        public int EffortsCount { get; set; }

        public bool FullDetailsLoaded { get; set; }

        public ObservableCollection<Effort> Efforts { get; set; }

        public void PopulateEfforts()
        {
            if (Efforts == null)
            {
                Efforts = new ObservableCollection<Effort>();

                IsolatedStorageFile myIsolatedStorage = IsolatedStorageFile.GetUserStoreForApplication();
                IsolatedStorageFileStream fileStream;

                if (myIsolatedStorage.FileExists("rideEfforts-" + Id.ToString() + ".txt"))
                {
                    fileStream = myIsolatedStorage.OpenFile("rideEfforts-" + Id.ToString() + ".txt", FileMode.Open, FileAccess.Read);
                    string streamStr = "";
                    using (StreamReader reader = new StreamReader(fileStream))
                    {
                        streamStr = reader.ReadLine();
                    }
                    var serializer = new DataContractJsonSerializer(typeof(ObservableCollection<Effort>));
                    MemoryStream stream = new MemoryStream(Encoding.UTF8.GetBytes(streamStr));
                    Efforts = new ObservableCollection<Effort>();
                    Efforts = (ObservableCollection<Effort>)serializer.ReadObject(stream);

                    if (Efforts != null)
                    {
                        foreach (Effort effort in Efforts)
                        {
                            effort.Segment = App.ViewModel.DoesSegmentExist(effort.SegmentId);
                            effort.Athlete = App.ViewModel.me;
                        }
                    }

                }

                EffortsCVS = new CollectionViewSource();
                EffortsCVS.Source = Efforts;
                EffortsCVS.SortDescriptions.Add(new SortDescription("Segment.TopPlacing", ListSortDirection.Ascending));
                EffortsCVS.SortDescriptions.Add(new SortDescription("Segment.PlacingCount", ListSortDirection.Descending));
                EffortsCVS.SortDescriptions.Add(new SortDescription("Segment.ElevationDifference", ListSortDirection.Descending));
                if ((Efforts != null) && (Efforts.Count > 0))
                {
                    EffortsCVS.View.MoveCurrentToPosition(-1);
                    EffortsCVS.View.Refresh();
                }
            }
        }


        public void PopulateMap()
        {
            if (MapLocations == null)
            {
                MapLocations = new ObservableCollection<MapPoint>();

                IsolatedStorageFile myIsolatedStorage = IsolatedStorageFile.GetUserStoreForApplication();
                IsolatedStorageFileStream fileStream;

                if (myIsolatedStorage.FileExists("rideMap-" + Id.ToString() + ".txt"))
                {
                    fileStream = myIsolatedStorage.OpenFile("rideMap-" + Id.ToString() + ".txt", FileMode.Open, FileAccess.Read);
                    string streamStr = "";
                    using (StreamReader reader = new StreamReader(fileStream))
                    {
                        streamStr = reader.ReadLine();
                    }
                    var serializer = new DataContractJsonSerializer(typeof(ObservableCollection<MapPoint>));
                    MemoryStream stream = new MemoryStream(Encoding.UTF8.GetBytes(streamStr));
                    MapLocations = new ObservableCollection<MapPoint>();
                    MapLocations = (ObservableCollection<MapPoint>)serializer.ReadObject(stream);
                }
            }
        }

        public CollectionViewSource EffortsCVS { get; set; }

        public ObservableCollection<MapPoint> MapLocations { get; set; }
        public List<List<MapPoint>> MapPointLists { get; set; }

        RideDetail _detail;
        [DataMember(Name = "Detail")]
        public RideDetail Detail
        {
            get
            {
                return _detail;
            }
            set
            {
                if (value != _detail)
                {
                    _detail = value;
                    NotifyPropertyChanged("Detail");
                }
            }
        }

        //public ObservableCollection<Segment> Segments { get; set; }
        //public CollectionViewSource SegmentsCVS { get; set; }

        [DataMember(Name = "OverallFirstPositionCount")]
        public int OverallFirstPositionCount { get; set; }
        [DataMember(Name = "OverallSecondPositionCount")]
        public int OverallSecondPositionCount { get; set; }
        [DataMember(Name = "OverallThirdPositionCount")]
        public int OverallThirdPositionCount { get; set; }
        [DataMember(Name = "OverallOtherPositionCount")]
        public int OverallOtherPositionCount { get; set; }

        [DataMember(Name = "MyFirstPositionCount")]
        public int MyFirstPositionCount { get; set; }
        [DataMember(Name = "MySecondPositionCount")]
        public int MySecondPositionCount { get; set; }
        [DataMember(Name = "MyThirdPositionCount")]
        public int MyThirdPositionCount { get; set; }
        [DataMember(Name = "MyOtherPositionCount")]
        public int MyOtherPositionCount { get; set; }

        public void CalculatePositions(string type)
        {
            if (type == "Overall")
            {
                OverallFirstPositionCount = 0;
                OverallSecondPositionCount = 0;
                OverallThirdPositionCount = 0;
                OverallOtherPositionCount = 0;
            }
            else
            {
                MyFirstPositionCount = 0;
                MySecondPositionCount = 0;
                MyThirdPositionCount = 0;
                MyOtherPositionCount = 0;
            }

            PopulateEfforts();

            foreach (Effort effort in Efforts)
            {
                effort.Segment.PopulateEfforts();
                effort.Segment.PopulateMyEfforts();

                if ((type == "Overall") &&
                    (effort.Segment.Efforts != null))
                {
                    int counter = 1;
                    foreach (Effort effort2 in from e in effort.Segment.Efforts
                                               where e.Athlete != App.ViewModel.me
                                               orderby e.ElapsedTime
                                               select e)
                    {
                        if (effort.ElapsedTime <= effort2.ElapsedTime)
                        {
                            break;
                        }
                        counter++;
                        if (counter > 3)
                        {
                            break;
                        }
                    }
                    switch (counter)
                    {
                        case 1:
                            OverallFirstPositionCount++;
                            break;
                        case 2:
                            OverallSecondPositionCount++;
                            break;
                        case 3:
                            OverallThirdPositionCount++;
                            break;
                        default:
                            OverallOtherPositionCount++;
                            break;
                    }

                }

                if ((type == "MyResults") &&
                    (effort.Segment.MyEfforts != null))
                {
                    int counter = 1;
                    foreach (Effort effort2 in effort.Segment.MyEfforts.OrderBy(e => e.ElapsedTime))
                    {
                        if (effort.ElapsedTime == effort2.ElapsedTime)
                        {
                            break;
                        }
                        counter++;
                    }

                    effort.MyPlacing = counter;

                    switch (counter)
                    {
                        case 1:
                            MyFirstPositionCount++;
                            break;
                        case 2:
                            MySecondPositionCount++;
                            break;
                        case 3:
                            MyThirdPositionCount++;
                            break;
                        default:
                            MyOtherPositionCount++;
                            break;
                    }

                }
            }

            NotifyPropertyChanged("MyFirstPositionCount");
            NotifyPropertyChanged("MySecondPositionCount");
            NotifyPropertyChanged("MyThirdPositionCount");
            NotifyPropertyChanged("MyOtherPositionCount");

            NotifyPropertyChanged("OverallFirstPositionCount");
            NotifyPropertyChanged("OverallSecondPositionCount");
            NotifyPropertyChanged("OverallThirdPositionCount");
            NotifyPropertyChanged("OverallOtherPositionCount");
        }

        private double maxElevation = 0;
        private double minElevation = 0;
        public PathGeometry ElevationLineData
        {
            get
            {
                PathGeometry pg = new PathGeometry();

                if (Locations[0].Altitude == 0.0)
                {
                    return null;
                }

                maxElevation = Math.Ceiling((from mp in MapLocations
                                             select mp.Elv).Max());

                minElevation = Math.Floor((from mp in MapLocations
                                           select mp.Elv).Min());

                double screenWidth = 480.0 - 12.0 - 12.0;
                int controlHeight = 100;
                double divisor = double.Parse(MapLocations.Count.ToString()) / screenWidth;

                PathFigure pf = new PathFigure();
                pf.StartPoint = new Point(0, controlHeight);

                LineSegment ls1 = new LineSegment();
                ls1.Point = new Point(0, controlHeight - MapLocations[0].Elv * controlHeight / maxElevation);
                pf.Segments.Add(ls1);

                int counter = 1;
                int x = 1;
                foreach (MapPoint mp in MapLocations)
                {
                    LineSegment ls2 = new LineSegment();
                    ls2.Point = new Point(x / divisor, controlHeight - mp.Elv * controlHeight / maxElevation);
                    pf.Segments.Add(ls2);
                    x++;
                    //                        PathFigure start = new PathFigure();
                    //                        pg.Figures.Add(new PathFigure()
                    //}

                    counter++;
                }

                LineSegment ls3 = new LineSegment();
                ls3.Point = new Point(screenWidth, controlHeight);
                pf.Segments.Add(ls3);

                pg.Figures = new PathFigureCollection();
                pg.Figures.Add(pf);

                return pg;
            }
        }


        public List<string> latlngstr { get; set; }

        float minLat;
        float maxLat;
        float minLng;
        float maxLng;

        private LocationCollection _locations;
        public LocationCollection Locations
        {
            get
            {
                if ((_locations == null) ||
                    (_locations[0].Altitude != MapLocations[0].Elv))
                {
                    minLat = MapLocations[0].Lat;
                    maxLat = MapLocations[0].Lat;
                    minLng = MapLocations[0].Lng;
                    maxLng = MapLocations[0].Lng;

                    _locations = new LocationCollection();
                    MapPoint lastPoint = null;
                    foreach (MapPoint mp in MapLocations)
                    {
                        if (mp.Lat < minLat)
                        {
                            minLat = mp.Lat;
                        }
                        if (mp.Lat > maxLat)
                        {
                            maxLat = mp.Lat;
                        }
                        if (mp.Lng < minLng)
                        {
                            minLng = mp.Lng;
                        }
                        if (mp.Lng > maxLng)
                        {
                            maxLng = mp.Lng;
                        }

                        if ((lastPoint == null) ||
                        (App.ViewModel.DistanceBetween(lastPoint, mp) > 0.02))
                        {
                            _locations.Add(new GeoCoordinate(mp.Lat, mp.Lng, mp.Elv));
                            lastPoint = mp;
                        }
                    }

                    MapCenter = new GeoCoordinate(minLat + ((maxLat - minLat) / 2), minLng + ((maxLng - minLng) / 2));

                    //ZoomLevel = 12.0;

                    NotifyPropertyChanged("Locations");
                    NotifyPropertyChanged("MapCenter");
                    NotifyPropertyChanged("ZoomLevel");

                    return _locations;
                }
                return _locations;
            }
        }

        private bool populatingElevation = false;
        public void PopulateElevation()
        {
            if (this.MapLocations.Count == 0)
            {
                populatingElevation = true;
                PopulateMap();
            }
            else
            {
                if (this.MapLocations[0].Elv == 0)
                {
                    App.ViewModel.Busy = true;
                    App.ViewModel.BusyMessage = "loading elevations...";

                    latlngstr = new List<string>();
                    MapPointLists = new List<List<MapPoint>>();

                    int counter1 = -1;
                    int counter2 = 0;
                    foreach (MapPoint mp in MapLocations)
                    {
                        if (counter2 % 90 == 0)
                        {
                            counter1++;
                            latlngstr.Add("");
                            MapPointLists.Add(new List<MapPoint>());
                        }
                        latlngstr[counter1] += mp.Lat + "," + mp.Lng + "|";
                        counter2++;
                    }
                    for (int i = 0; i < latlngstr.Count; i++)
                    {
                        latlngstr[i] = latlngstr[i].TrimEnd('|');
                        App.ViewModel.wcElevationArr.Add(latlngstr[i]);
                    }

                    App.ViewModel.GetNextElevation();
                }
            }
        }

        public GeoCoordinate MapCenter { get; set; }

        private ObservableCollection<MapPolylineObj> _mapObjects;
        public ObservableCollection<MapPolylineObj> MapObjects
        {
            get
            {
                if (_mapObjects != null)
                {
                    return _mapObjects;
                }
                _mapObjects = new ObservableCollection<MapPolylineObj>();
                MapPolylineObj mpo1 = new MapPolylineObj();
                mpo1.Locations = Locations;
                mpo1.StrokeThickness = 2;
                mpo1.Stroke = new SolidColorBrush(Color.FromArgb(255, 137, 39, 0));

                _mapObjects.Add(mpo1);

                foreach (Effort effort in Efforts)
                {
                    effort.Segment.PopulateMap(this);
                    MapPolylineObj mpo2 = new MapPolylineObj();
                    mpo2.Locations = effort.Segment.Locations;
                    mpo2.StrokeThickness = 2;

                    switch (effort.Segment.TopPlacing)
                    {
                        case 1:
                            mpo2.Stroke = new SolidColorBrush(Colors.Green);
                            break;
                        case 2:
                            mpo2.Stroke = new SolidColorBrush(Colors.Yellow);
                            break;
                        case 3:
                            mpo2.Stroke = new SolidColorBrush(Colors.Purple);
                            break;
                        default:
                            mpo2.Stroke = new SolidColorBrush(Colors.Red);
                            break;
                    }

                    _mapObjects.Add(mpo2);
                }

                return _mapObjects;
            }
        }

        private ObservableCollection<PathGeometryObj> _profilePathObjects;
        public ObservableCollection<PathGeometryObj> ProfilePathObjects
        {
            get
            {
                /*if (_profilePathObjects != null)
                {
                    return _profilePathObjects;
                }*/

                /*IsolatedStorageFile myIsolatedStorage = IsolatedStorageFile.GetUserStoreForApplication();
                IsolatedStorageFileStream fileStream;

                if (myIsolatedStorage.FileExists("rideProfilePathObjects-" + Id.ToString() + ".txt"))
                {
                    fileStream = myIsolatedStorage.OpenFile("rideProfilePathObjects-" + Id.ToString() + ".txt", FileMode.Open, FileAccess.Read);
                    string streamStr = "";
                    using (StreamReader reader = new StreamReader(fileStream))
                    {
                        streamStr = reader.ReadLine();
                    }
                    var serializer = new DataContractJsonSerializer(typeof(ObservableCollection<PathGeometryObj>));
                    MemoryStream stream = new MemoryStream(Encoding.UTF8.GetBytes(streamStr));
                    _profilePathObjects = new ObservableCollection<PathGeometryObj>();
                    _profilePathObjects = (ObservableCollection<PathGeometryObj>)serializer.ReadObject(stream);

                    return _profilePathObjects;
                }*/

                _profilePathObjects = new ObservableCollection<PathGeometryObj>();
                PathGeometryObj pgo1 = new PathGeometryObj();
                pgo1.PathGeometry = ElevationLineData;
                pgo1.StrokeThickness = 2;
                pgo1.Stroke = new SolidColorBrush(Color.FromArgb(255, 137, 39, 0));
                pgo1.Fill = (Brush)Application.Current.Resources["RideProfileFill"];
                pgo1.MyPlacing = 0;
                pgo1.Overall = 0;

                _profilePathObjects.Add(pgo1);

                foreach (Effort effort in Efforts)
                {
                    effort.Segment.PopulateEfforts();
                    if (effort.OverallPlacing == 0)
                    {
                        CalculatePositions("Overall");
                    }
                    if (effort.MyPlacing == 0)
                    {
                        CalculatePositions("MyResults");
                    }
                    int startPos = effort.Segment.PopulateMap(this);
                    PathGeometryObj pgo2 = new PathGeometryObj();
                    pgo2.PathGeometry = effort.Segment.GetElevationLineData(minElevation, maxElevation, "Line", startPos, this);
                    pgo2.StrokeThickness = 2;

                    switch (effort.Segment.TopPlacing)
                    {
                        case 1:
                            pgo2.Stroke = new SolidColorBrush(Colors.Green);
                            break;
                        case 2:
                            pgo2.Stroke = new SolidColorBrush(Colors.Yellow);
                            break;
                        case 3:
                            pgo2.Stroke = new SolidColorBrush(Colors.Purple);
                            break;
                        default:
                            pgo2.Stroke = new SolidColorBrush(Colors.Red);
                            break;
                    }
                    pgo2.MyPlacing = effort.MyPlacing;
                    pgo2.Overall = effort.Segment.Efforts.Count;
                    //pgo2.Fill = (Brush)Application.Current.Resources["SegmentProfileFill"];

                    _profilePathObjects.Add(pgo2);
                }

                //App.ViewModel.SaveCache(_profilePathObjects, "rideProfilePathObjects-" + Id.ToString());
                return _profilePathObjects;
            }
        }

        private double _zoomLevel;
        public double ZoomLevel
        {
            get
            {
                if (_zoomLevel == 0)
                {
                    if (minLat == 0.0F)
                    {
                        LocationCollection locations = Locations;
                    }

                    float maxSpan = Math.Max(maxLat - minLat, maxLng - minLng);
                    _zoomLevel = Math.Min(8 / Math.Sqrt(Math.Sqrt(Math.Sqrt(maxSpan))), 19.0);
                }
                return _zoomLevel;
            }
        }

        internal void SetMapLocations()
        {
            MapLocations = new ObservableCollection<MapPoint>();
            foreach (List<MapPoint> mpl in MapPointLists)
            {
                MapLocations = new ObservableCollection<MapPoint>(MapLocations.Concat(new ObservableCollection<MapPoint>(mpl)));
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;
        public void NotifyPropertyChanged(String propertyName)
        {
            PropertyChangedEventHandler handler = PropertyChanged;
            if (null != handler)
            {
                handler(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }

    [DataContract]
    public class RideMap
    {
        [DataMember(Name = "isDirty")]
        public bool IsDirty { get; set; }

        [DataMember(Name = "id")]
        public int RideId { get; set; }
        [DataMember(Name = "latlng")]
        public List<List<float>> LatLng { get; set; }

        private ObservableCollection<MapPoint> _mapLocations;
        public ObservableCollection<MapPoint> MapLocations
        {
            get
            {
                if (_mapLocations != null)
                {
                    return _mapLocations;
                }

                _mapLocations = new ObservableCollection<MapPoint>();

                foreach (List<float> point in LatLng)
                {
                    MapPoint mp = new MapPoint();
                    mp.Lat = point[0];
                    mp.Lng = point[1];
                    mp.Id = mp.Lat.ToString() + "/" + mp.Lng.ToString();

                    _mapLocations.Add(mp);

                    //App.ViewModel.wcElevationArr.Add(mp.Id);
                    //App.ViewModel.GetNextElevation();

                    App.ViewModel.AllMapPoints.Add(mp);
                }

                return _mapLocations;
            }
        }
    }

    [DataContract]
    public class MapPoint : INotifyPropertyChanged
    {
        [DataMember(Name = "Id")]
        public string Id { get; set; }
        [DataMember(Name = "Lat")]
        public float Lat { get; set; }
        [DataMember(Name = "Lng")]
        public float Lng { get; set; }
        [DataMember(Name = "Elv")]
        public float Elv { get; set; }
        [DataMember(Name = "RideId")]
        public int RideId { get; set; }

        public event PropertyChangedEventHandler PropertyChanged;
        public void NotifyPropertyChanged(String propertyName)
        {
            PropertyChangedEventHandler handler = PropertyChanged;
            if (null != handler)
            {
                handler(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }

    [DataContract]
    public class RideDetail : INotifyPropertyChanged
    {
        [DataMember(Name = "id")]
        public int Id { get; set; }

        [DataMember(Name = "name")]
        public string Name { get; set; }

        [DataMember(Name = "startDate")]
        public DateTime StartDate { get; set; }

        [DataMember(Name = "elapsedTime")]
        public int ElapsedTime { get; set; }

        [DataMember(Name = "movingTime")]
        public int MovingTime { get; set; }

        [DataMember(Name = "distance")]
        public float Distance { get; set; }

        [DataMember(Name = "averageSpeed")]
        public float AverageSpeed { get; set; }

        [DataMember(Name = "averageWatts")]
        public float AverageWatts { get; set; }

        [DataMember(Name = "maximumSpeed")]
        public float MaximumSpeed { get; set; }

        [DataMember(Name = "elevationGain")]
        public float ElevationGain { get; set; }

        [DataMember(Name = "location")]
        public string Location { get; set; }

        [DataMember(Name = "bike")]
        public Bike Bike { get; set; }

        //[DataMember(Name = "athlete")]
        //public Athlete Athlete { get; set; }

        [DataMember(Name = "description")]
        public string Description { get; set; }

        public event PropertyChangedEventHandler PropertyChanged;
        public void NotifyPropertyChanged(String propertyName)
        {
            PropertyChangedEventHandler handler = PropertyChanged;
            if (null != handler)
            {
                handler(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }

    [DataContract]
    public class Athlete
    {
        [DataMember(Name = "name")]
        public string Name { get; set; }
        [DataMember(Name = "id")]
        public int Id { get; set; }
        [DataMember(Name = "username")]
        public string Username { get; set; }
    }

    [DataContract]
    public class Bike
    {
        [DataMember(Name = "name")]
        public string Name { get; set; }
        [DataMember(Name = "id")]
        public int Id { get; set; }
    }

    [DataContract]
    public class Segment : INotifyPropertyChanged
    {
        public Segment()
        {
            IsDirty = true;
            IsEffortsDirty = true;
            IsMyEffortsDirty = true;
            MyEffortsCVS = new CollectionViewSource();
        }
        [DataMember(Name = "isDirty")]
        public bool IsDirty { get; set; }
        public bool IsEffortsDirty { get; set; }
        public bool IsMyEffortsDirty { get; set; }

        [DataMember(Name = "name")]
        public string Name { get; set; }
        [DataMember(Name = "id")]
        public int Id { get; set; }
        [DataMember(Name = "distance")]
        public float Distance { get; set; }
        [DataMember(Name = "avg_grade")]
        public float AverageGrade { get; set; }
        [DataMember(Name = "climb_category")]
        public string ClimbCategory { get; set; }
        [DataMember(Name = "elev_difference")]
        public float ElevationDifference { get; set; }
        [DataMember(Name = "start_latlng")]
        public float[] StartLatLong { get; set; }
        [DataMember(Name = "end_latlng")]
        public float[] EndLatLong { get; set; }

        public int _topPlacing;
        [DataMember(Name = "TopPlacing")]
        public int TopPlacing
        {
            get
            {
                return _topPlacing;
            }
            set
            {
                if (value != _topPlacing)
                {
                    _topPlacing = value;
                    NotifyPropertyChanged("TopPlacing");
                }
            }
        }

        public int _placingCount;
        [DataMember(Name = "PlacingCount")]
        public int PlacingCount
        {
            get
            {
                return _placingCount;
            }
            set
            {
                if (value != _placingCount)
                {
                    _placingCount = value;
                    NotifyPropertyChanged("PlacingCount");
                }
            }
        }

        private Effort _myFastestEffort = null;
        public Effort MyFastestEffort
        {
            get
            {
                if (_myFastestEffort == null)
                {
                    PopulateMyEfforts();
                    _myFastestEffort = (from e in MyEfforts
                                        orderby e.ElapsedTime ascending
                                        select e).FirstOrDefault();
                }
                return _myFastestEffort;
            }
        }

        private Effort _komFastestEffort = null;
        public Effort KOMFastestEffort
        {
            get
            {
                if (_komFastestEffort == null)
                {
                    PopulateEfforts();
                    _komFastestEffort = (from e in Efforts
                                         orderby e.ElapsedTime ascending
                                         select e).FirstOrDefault();
                }
                return _komFastestEffort;
            }
        }
        
        public double MyVAM
        {
            get
            {
                Effort myEff = MyFastestEffort;
                if ((myEff == null) ||
                    (Distance * AverageGrade < 8000) ||
                    (AverageGrade < 0) ||
                    (AverageGrade > 25))
                {
                    return 0;
                }
                return (ElevationDifference * 60.0) / (myEff.ElapsedTime / 60.0);
            }
        }

        public double MyRelativePower
        {
            get
            {
                return MyVAM / (2 + (AverageGrade / 10)); //*100
            }
        }
        public double KOMVAM
        {
            get
            {
                Effort komEff = KOMFastestEffort;
                if ((komEff == null) ||
                    (Distance * AverageGrade < 8000) ||
                    (AverageGrade < 0) ||
                    (AverageGrade > 25))
                {
                    return 0;
                }
                return (ElevationDifference * 60.0) / (komEff.ElapsedTime / 60.0);
            }
        }

        public double KOMRelativePower
        {
            get
            {
                return KOMVAM / (2 + (AverageGrade / 10)); //*100
            }
        }

        public int ClimbCategoryOrder
        {
            get
            {
                if (ClimbCategory == "HC")
                {
                    return 0;
                }
                if (ClimbCategory == "NC")
                {
                    return 5;
                }
                else return int.Parse(ClimbCategory);
            }
        }

        //public ObservableCollection<Segment> Segments { get; set; }
        //[DataMember(Name = "Efforts")]
        public ObservableCollection<Effort> Efforts { get; set; }

        //[DataMember(Name = "MyEfforts")]
        public ObservableCollection<Effort> MyEfforts { get; set; }
        public CollectionViewSource MyEffortsCVS { get; set; }

        public void PopulateEfforts()
        {
            if (Efforts == null)
            {
                Efforts = new ObservableCollection<Effort>();

                IsolatedStorageFile myIsolatedStorage = IsolatedStorageFile.GetUserStoreForApplication();
                IsolatedStorageFileStream fileStream;

                if (myIsolatedStorage.FileExists("segmentEfforts-" + Id.ToString() + ".txt"))
                {
                    fileStream = myIsolatedStorage.OpenFile("segmentEfforts-" + Id.ToString() + ".txt", FileMode.Open, FileAccess.Read);
                    string streamStr = "";
                    using (StreamReader reader = new StreamReader(fileStream))
                    {
                        streamStr = reader.ReadLine();
                    }
                    var serializer = new DataContractJsonSerializer(typeof(ObservableCollection<Effort>));
                    MemoryStream stream = new MemoryStream(Encoding.UTF8.GetBytes(streamStr));
                    Efforts = new ObservableCollection<Effort>();
                    Efforts = (ObservableCollection<Effort>)serializer.ReadObject(stream);
                }
            }
        }

        public int MyEffortsCount { get; set; }

        public void PopulateMyEfforts()
        {
            if (MyEfforts == null)
            {
                MyEfforts = new ObservableCollection<Effort>();

                IsolatedStorageFile myIsolatedStorage = IsolatedStorageFile.GetUserStoreForApplication();
                IsolatedStorageFileStream fileStream;

                if (myIsolatedStorage.FileExists("segmentMyEfforts-" + Id.ToString() + ".txt"))
                {
                    fileStream = myIsolatedStorage.OpenFile("segmentMyEfforts-" + Id.ToString() + ".txt", FileMode.Open, FileAccess.Read);
                    string streamStr = "";
                    using (StreamReader reader = new StreamReader(fileStream))
                    {
                        streamStr = reader.ReadLine();
                    }
                    var serializer = new DataContractJsonSerializer(typeof(ObservableCollection<Effort>));
                    MemoryStream stream = new MemoryStream(Encoding.UTF8.GetBytes(streamStr));
                    MyEfforts = new ObservableCollection<Effort>();
                    MyEfforts = (ObservableCollection<Effort>)serializer.ReadObject(stream);
                }
                MyEffortsCount = MyEfforts.Count;
                NotifyPropertyChanged("MyEffortsCount");
            }
            MyEffortsCVS = new CollectionViewSource();
            MyEffortsCVS.Source = MyEfforts;
            MyEffortsCVS.SortDescriptions.Add(new SortDescription("OverallPlacing", ListSortDirection.Ascending));
            MyEffortsCVS.View.MoveCurrentTo(-1);
            MyEffortsCVS.View.Refresh();
        }

        [DataMember(Name = "TimeDifferenceToBest")]
        public int TimeDifferenceToBest { get; set; }

        public void PopulateTimeDifference()
        {
            if (TopPlacing == 1)
            {
                TimeDifferenceToBest = 1000000;
            }
            else
            {
                /*if (TimeDifferenceToBest == 0)
                {
                    PopulateEfforts();
                    PopulateMyEfforts();
                    if (MyEfforts.Count > 0)
                    {*/
                Effort effort = this.Efforts.FirstOrDefault(eff => eff.Athlete.Username == App.ViewModel.Username);
                if (effort == null)
                {
                    TimeDifferenceToBest = 9999;
                }
                else
                {
                    int myBest = effort.ElapsedTime;
                    int best = this.Efforts[0].ElapsedTime;

                    TimeDifferenceToBest = myBest - best;
                }
                /*}
            }*/
            }
            NotifyPropertyChanged("TimeDifferenceToBest");
        }

        float minLat;
        float maxLat;
        float minLng;
        float maxLng;

        private LocationCollection _locations;
        public LocationCollection Locations
        {
            get
            {
                if (MapLocations == null)
                {
                    return new LocationCollection();
                }
                if (MapLocations.Count == 0)
                {
                    PopulateMap();
                    if (MapLocations.Count == 0)
                    {
                        return null;
                    }
                }
                if ((_locations == null) ||
                    (_locations[0].Altitude != MapLocations[0].Elv))
                {
                    minLat = MapLocations[0].Lat;
                    maxLat = MapLocations[0].Lat;
                    minLng = MapLocations[0].Lng;
                    maxLng = MapLocations[0].Lng;

                    _locations = new LocationCollection();
                    MapPoint lastPoint = null;
                    foreach (MapPoint mp in MapLocations)
                    {
                        if (mp.Lat < minLat)
                        {
                            minLat = mp.Lat;
                        }
                        if (mp.Lat > maxLat)
                        {
                            maxLat = mp.Lat;
                        }
                        if (mp.Lng < minLng)
                        {
                            minLng = mp.Lng;
                        }
                        if (mp.Lng > maxLng)
                        {
                            maxLng = mp.Lng;
                        }

                        if ((lastPoint == null) ||
                            (App.ViewModel.DistanceBetween(lastPoint, mp) > 0.02))
                        {
                            _locations.Add(new GeoCoordinate(mp.Lat, mp.Lng, mp.Elv));
                            lastPoint = mp;
                        }
                    }

                    MapCenter = new GeoCoordinate(minLat + ((maxLat - minLat) / 2), minLng + ((maxLng - minLng) / 2));

                    //ZoomLevel = 12.0;

                    NotifyPropertyChanged("Locations");
                    NotifyPropertyChanged("MapCenter");
                    NotifyPropertyChanged("ZoomLevel");

                    return _locations;
                }
                return _locations;
            }
        }


        public int PopulateMap(Ride optionalRide = null)
        {
            // check if already cached
            /*            IsolatedStorageFile myIsolatedStorage = IsolatedStorageFile.GetUserStoreForApplication();
                        IsolatedStorageFileStream fileStream;

                        if (myIsolatedStorage.FileExists("segmentMap-" + Id.ToString() + ".txt"))
                        {
                            fileStream = myIsolatedStorage.OpenFile("segmentMap-" + Id.ToString() + ".txt", FileMode.Open, FileAccess.Read);
                            string streamStr = "";
                            using (StreamReader reader = new StreamReader(fileStream))
                            {
                                streamStr = reader.ReadLine();
                            }
                            var serializer = new DataContractJsonSerializer(typeof(ObservableCollection<MapPoint>));
                            MemoryStream stream = new MemoryStream(Encoding.UTF8.GetBytes(streamStr));
                            MapLocations = new ObservableCollection<MapPoint>();
                            MapLocations = (ObservableCollection<MapPoint>)serializer.ReadObject(stream);
                        }
                        else
                        {*/
            MapLocations = new ObservableCollection<MapPoint>();
            // find ride containing segment
            Ride ride = new Ride();

            if (optionalRide == null)
            {
                ride = MyEfforts[0].Ride;
            }
            else
            {
                ride = optionalRide;
            }

            /*foreach (Ride ride2 in App.ViewModel.Rides)
            {
                ride2.PopulateEfforts();
                Effort effort = (from eff in ride2.Efforts
                                 where eff.SegmentId == Id
                                 select eff).FirstOrDefault();
                if (effort != null)
                {
                    ride = ride2;
                    break;
                }
            }*/

            // look for start of segment in map locations
            MapPoint startMapPoint = new MapPoint();
            startMapPoint.Lat = StartLatLong[0];
            startMapPoint.Lng = StartLatLong[1];
            MapPoint endMapPoint = new MapPoint();
            endMapPoint.Lat = EndLatLong[0];
            endMapPoint.Lng = EndLatLong[1];

            int startPoint = 0;
            int endPoint = 0;
            bool startFound = false;

            ride.PopulateMap();

            for (int i = 0; i < ride.MapLocations.Count; i++)
            {
                MapPoint mp = ride.MapLocations[i];

                //Debug.WriteLine(i.ToString() + " - " + distance.ToString() + "km");

                if (!startFound)
                {
                    double distanceFromStart = App.ViewModel.DistanceBetween(startMapPoint, mp);

                    if (distanceFromStart < 0.05)
                    {
                        startPoint = i;
                        startFound = true;
                        MapLocations.Add(mp);
                    }
                }
                else
                {
                    double distanceFromStart = App.ViewModel.DistanceBetween(startMapPoint, mp);
                    double distanceFromEnd = App.ViewModel.DistanceBetween(endMapPoint, mp);

                    MapLocations.Add(mp);

                    if ((i > startPoint + 5) && (distanceFromStart < 0.05))
                    {
                        startPoint = i;
                        MapLocations = new ObservableCollection<MapPoint>();
                        MapLocations.Add(mp);
                    }

                    if ((i > startPoint + 5) && (distanceFromEnd < 0.05))
                    {
                        endPoint = i;
                        break;
                    }
                }
            }
            //            }

            //App.ViewModel.SaveCache(MapLocations, "segmentMap-" + Id.ToString() + ".txt");
            return startPoint;
        }

        public GeoCoordinate MapCenter { get; set; }

        private double _zoomLevel;
        public double ZoomLevel
        {
            get
            {
                if (_zoomLevel == 0)
                {
                    if (minLat == 0.0F)
                    {
                        LocationCollection locations = Locations;
                    }

                    float maxSpan = Math.Max(maxLat - minLat, maxLng - minLng);
                    _zoomLevel = Math.Min(8 / Math.Sqrt(Math.Sqrt(Math.Sqrt(maxSpan))), 19.0);
                }
                return _zoomLevel;
            }
        }

        public ObservableCollection<MapPoint> MapLocations { get; set; }


        public event PropertyChangedEventHandler PropertyChanged;
        public void NotifyPropertyChanged(String propertyName)
        {
            PropertyChangedEventHandler handler = PropertyChanged;
            if (null != handler)
            {
                handler(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        internal PathGeometry GetElevationLineData(double minElevation, double maxElevation, string type, int startIndex, Ride ride = null)
        {
            if ((MapLocations == null) || (MapLocations.Count == 0))
            {
                PopulateMap(ride);
            }

            PathGeometry pg = new PathGeometry();

            double screenWidth = 480.0 - 12.0 - 12.0;
            int controlHeight = 100;
            double divisor = 0.0;

            if (ride != null)
            {
                divisor = double.Parse(ride.MapLocations.Count.ToString()) / screenWidth;
            }
            else
            {
                divisor = double.Parse(MapLocations.Count.ToString()) / screenWidth;
            }

            PathFigure pf = new PathFigure();

            if (MapLocations.Count > 0)
            {
                if (type == "Line")
                {
                    pf.StartPoint = new Point(startIndex / divisor, controlHeight - MapLocations[0].Elv * controlHeight / maxElevation);
                }
                else
                {
                    pf.StartPoint = new Point(startIndex / divisor, controlHeight);

                    LineSegment ls1 = new LineSegment();
                    ls1.Point = new Point(startIndex / divisor, controlHeight - MapLocations[0].Elv * controlHeight / maxElevation);
                    pf.Segments.Add(ls1);
                }

                int counter = 1;
                int x = 1;
                foreach (MapPoint mp in MapLocations)
                {
                    LineSegment ls2 = new LineSegment();
                    ls2.Point = new Point((startIndex + x) / divisor, controlHeight - mp.Elv * controlHeight / maxElevation);
                    pf.Segments.Add(ls2);
                    x++;
                    //                        PathFigure start = new PathFigure();
                    //                        pg.Figures.Add(new PathFigure()
                    //}

                    counter++;
                }

                if (type != "Line")
                {
                    LineSegment ls3 = new LineSegment();
                    ls3.Point = new Point((startIndex + x) / divisor, controlHeight);
                    pf.Segments.Add(ls3);
                }
            }

            pg.Figures = new PathFigureCollection();
            pg.Figures.Add(pf);

            return pg;

        }
    }

    [DataContract]
    public class Effort
    {
        [DataMember(Name = "segmentId")]
        public int SegmentId { get; set; }
        [DataMember(Name = "id")]
        public int Id { get; set; }
        [DataMember(Name = "activityId")]
        public int ActivityId { get; set; }
        [DataMember(Name = "startDate")]
        public DateTime StartDate { get; set; }
        [DataMember(Name = "start_date_local")]
        public DateTime StartDateLocal { get; set; }
        [DataMember(Name = "timeZoneOffset")]
        public int TimeZoneOffset { get; set; }
        [DataMember(Name = "elapsed_time")]
        public int ElapsedTime { get; set; }
        [DataMember(Name = "moving_time")]
        public int MovingTime { get; set; }
        [DataMember(Name = "average_speed")]
        public float AverageSpeed { get; set; }
        [DataMember(Name = "distance")]
        public float Distance { get; set; }
        [DataMember(Name = "athlete")]
        public Athlete Athlete { get; set; }
        [DataMember(Name = "OverallPlacing")]
        public int OverallPlacing { get; set; }
        [DataMember(Name = "MyPlacing")]
        public int MyPlacing { get; set; }

        public double VAM
        {
            get
            {
                if (Segment == null)
                {
                    Segment = App.ViewModel.Segments.FirstOrDefault(s => s.Id == SegmentId);
                }
                if ((Segment == null) ||
                    (Segment.Distance * Segment.AverageGrade < 8000) ||
                    (Segment.AverageGrade < 0) ||
                    (Segment.AverageGrade > 25))
                {
                    return 0;
                }
                return (Segment.ElevationDifference * 60.0) / (ElapsedTime / 60.0);
            }
        }

        public double RelativePower
        {
            get
            {
                return VAM / (2 + (Segment.AverageGrade / 10)); //*100
            }
        }

        public DateTime _startDateLocal2;
        [DataMember(Name = "startDateLocal")]
        public DateTime StartDateLocal2
        {
            get
            {
                return _startDateLocal2;
            }
            set
            {
                if (_startDateLocal2 != value)
                {
                    _startDateLocal2 = value;
                    StartDateLocal = value;
                }
            }
        }

        public int _elapsedTime2;
        [DataMember(Name = "elapsedTime")]
        public int ElapsedTime2
        {
            get
            {
                return _elapsedTime2;
            }
            set
            {
                if (_elapsedTime2 != value)
                {
                    _elapsedTime2 = value;
                    ElapsedTime = value;
                }
            }
        }

        [DataMember(Name = "ride")]
        public Ride Ride2 { get; set; }

        public Segment Segment { get; set; }

        private Ride _ride = null;
        public Ride Ride
        {
            get
            {
                if (Athlete.Username == App.ViewModel.Username)
                {
                    if (_ride == null)
                    {
                        _ride = App.ViewModel.Rides.FirstOrDefault(r => r.Id == ActivityId);
                    }
                    return _ride;
                }
                return null;
            }
        }
    }

    [DataContract]
    public class EffortDetail
    {
        [DataMember(Name = "effort")]
        public Effort Effort { get; set; }
        [DataMember(Name = "ride")]
        public Ride Ride { get; set; }
    }


    [DataContract]
    public class EffortDetails
    {
        [DataMember(Name = "elapsed_time")]
        public float Elapsed_time { get; set; }
        [DataMember(Name = "segment")]
        public Segment Segment { get; set; }
        [DataMember(Name = "id")]
        public int Id { get; set; }
    }

    [DataContract]
    public class RideEfforts
    {
        [DataMember(Name = "id")]
        public int RideId { get; set; }
        [DataMember(Name = "efforts")]
        public ObservableCollection<EffortSegment> EffortSegments { get; set; }
    }

    [DataContract]
    public class EffortSegment
    {
        [DataMember(Name = "effort")]
        public Effort Effort { get; set; }
        [DataMember(Name = "segment")]
        public Segment Segment { get; set; }
    }

    [DataContract]
    public class SegmentEfforts
    {
        [DataMember(Name = "segment")]
        public Segment Segment { get; set; }
        [DataMember(Name = "efforts")]
        public ObservableCollection<Effort> EffortsList { get; set; }
    }

    [DataContract]
    public class ElevationResult
    {
        [DataMember(Name = "elevation")]
        public float Elevation { get; set; }
        [DataMember(Name = "location")]
        public ElevationLocation Location { get; set; }
    }

    [DataContract]
    public class ElevationLocation
    {
        [DataMember(Name = "lat")]
        public float Lat { get; set; }
        [DataMember(Name = "lng")]
        public float Lng { get; set; }
    }

    public class Alert
    {
        public DateTime FoundDateTime { get; set; }
        public string Message { get; set; }
        public string Type { get; set; }
        public int Id { get; set; }
    }

    public class MapPolylineObj
    {
        public LocationCollection Locations { get; set; }
        public Brush Stroke { get; set; }
        public Brush Fill { get; set; }
        public double StrokeThickness { get; set; }
    }

    [KnownType(typeof(LinearGradientBrush))]
    [DataContract]
    public class PathGeometryObj
    {
        [DataMember(Name = "PathGeometry")]
        public PathGeometry PathGeometry { get; set; }
        [DataMember(Name = "Stroke")]
        public Brush Stroke { get; set; }
        [DataMember(Name = "Fill")]
        public Brush Fill { get; set; }
        [DataMember(Name = "StrokeThickness")]
        public double StrokeThickness { get; set; }
        [DataMember(Name = "MyPlacing")]
        public int MyPlacing { get; set; }
        [DataMember(Name = "Overall")]
        public int Overall { get; set; }
    }

    [DataContract]
    public class SegmentExplorerDetail
    {
        [DataMember(Name = "id")]
        public int Id { get; set; }
        [DataMember(Name = "num_times_all")]
        public int NumTimesAll { get; set; }
        [DataMember(Name = "num_times_athlete")]
        public int NumTimesAthlete { get; set; }
        [DataMember(Name = "points")]
        public string Points { get; set; }
    }
}
